﻿using System.Runtime.InteropServices;

namespace Model;

/******** PS二次封装数据的为议 ***********/
/**
 * PS<->上位机收发数据
 */
/** 类型
 * 0x20 业务数据
 * PS->PC
 * 0x00 心跳帧,定时发送
 * 0x10 登录
 * Ox11 离线
 * 0x12 当前所有的已登录节点
 * PC->PS
 * 0x80 询问当前已经连的节点
 * 在线升级
 * 0XA0 启动升级
 * OXA1 升级状态
 * 0xA2 一帧数据发送
 * 0xA3 一帧数据响应
 */
public enum PSPCType
{
    Data_Type = 0x20, // 0x20 业务数据
    Heart_Type = 0x00, // 0x00 心跳帧,定时发送
    Login_Type = 0x10, // 0x10 登录
    Logout_Type = 0x11, // 0x11 离线
    CurNodesReply_Type = 0x12, //0x12 当前所有的已登录节点
    CurNodesRequest_Type = 0x80, //0x80 询问当前已经连接的节点
    StartUpdateDev_Type = 0xA0, //0xA0 启动升级
    UpdateDevStatus_Type = 0xA1, // 0xA1 升级状态
    UpdateDevFrameSend_Type = 0xA2, //0xA2 一帧数据发送
    UpdateDevFrameReply_Type = 0xA3 //0xA3 一帧数据响应
}

// 升级状态枚举
public enum UpdateStatusEnum
{
    StartUpdateDev_Status = 0x00, //0x00:开始启动升级，接收数据
    ReceiveDone_Status = 0x01, // 0x01:数据接收完成，开始校验
    CheckDone_Status = 0x02, //0x02:校验完成，开始擦除Flash
    EraseFlashDone_Status = 0x03, //0x03:擦除Flash完成，开始写入Flash
    WriteFlashDone_Status = 0x04, //0x04:写入Flash完成，升级完成
    InvalidNodeId_Status = 0xE0, // 0xE0:节点错误,不存在的节点
    ReceiveError_Status = 0xF0, //0xF0:接收数据异常，退出升级
    CheckError_Status = 0xF1, // 0xF1:数据校验错误
    FlashError_Status = 0xF2, // 0xF2:Flash操作错误
    Unknown_Status = 0xFA //0XFA:未知错误
}

// 协议公共参数/函数
public static class ProtocolStructCommon
{
    // 有效数据的最大长度
    public const uint ZPS_PcDataMaxLength = 8192;

    /// <summary>
    /// 计算校验和(纯加和)
    /// </summary>
    /// <param name="data">数据</param>
    /// <param name="length">数据长度</param>
    /// <returns></returns>
    public static unsafe uint Checksum_Pure(byte* data, int length)
    {
        uint checksum = 0;
        for (var i = 0; i < length; ++i)
        {
            checksum += data[i];
        }

        return checksum;
    }

    // 计算校验和(纯加和)
    public static uint Checksum_Pure(Span<byte> data)
    {
        uint checksum = 0;
        foreach (var b in data)
        {
            checksum += b;
        }

        return checksum;
    }

    /// <summary>
    /// 结构体转换为字节数组
    /// </summary>
    public static unsafe byte[] StructToByteArray<T>(ref T arg) where T : struct
    {
        int size = Marshal.SizeOf(typeof(T));
        byte[] arr = new byte[size];

        // 获取结构体的字节表示
        fixed (byte* ptr = arr)
        {
            Marshal.StructureToPtr(arg, (IntPtr) ptr, false);
        }

        return arr;
    }

    /// <summary>
    /// 计算 ZPS_Pc 结构的帧头校验和(纯加和)
    /// </summary>
    public static unsafe uint Checksum_ZPS_PC(ZPS_Pc data)
    {
        uint checksum = 0;

        // 先获取 ZPS_Pc*,再转换为 byte*,否则不能用ref
        byte* ptr = (byte*) &data; // 显式转换为 byte*
        for (var i = 2; i < sizeof(ZPS_Pc) - 4; ++i)
        {
            checksum += ptr[i];
        }

        return checksum;
    }

    /// <summary>
    /// 计算异或校验和
    /// </summary>
    public static byte CheckXOR(IEnumerable<byte> data)
    {
        byte resule = 0;
        foreach (var b in data)
        {
            resule ^= b;
        }

        return resule;
    }
}

// 帧头结构
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public unsafe struct ZPS_Pc
{
    public fixed byte U8StartFlag[2]; // 帧头 2
    public byte U8Type = (byte) PSPCType.Data_Type; // 类型,默认业务数据 1
    public uint U32Length = 0; // 有效数据长度 4
    public ushort U16Id = 0; // 节点ID 2
    public uint U32DataChecksum = 0; // 数据校验和 4
    public ushort U16HeaderChecksum = 0; // 帧头校验和 2

    // 后面跟数据体 N

    public ZPS_Pc()
    {
        U8StartFlag[0] = 0x95;
        U8StartFlag[1] = 0x27;
    }
}

// 启动升级的数据体 0xA0
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UpdateStart
{
    public ushort U16Id; // 节点ID 2
    public uint U32FrameMaxLength; // 一帧数据最大长度(当最后一帧的时候,可能没有这么长)
    public uint U32TotalLength; // 总数据长度
    public uint U32TotalDataChecksum; // 总数据校验
    public uint U32TotalFrameCount; // 总帧数 4
}

// 升级状态的数据体 0xA1
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UpdateStatus
{
    // 升级状态 1
    public byte U8Status;
}

// 一帧数据发送的数据体 0xA2
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UpdateOneFrame
{
    public uint U32PageIdx; // 页索引 4

    // 具体的一帧数据 N
    // byte[] U8Data;
}

// 一帧数据响应的数据体 0xA3
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UpdateOneFrameResponse
{
    // 页索引,从0开始,到总帧数-1结束 4
    public uint U32FrameId;

    // 响应结果,0正常,1异常 1
    public byte U8Status;
}